// -----------------------------------------------------------------------
// <copyright file="ScrollView.cs" company="AillieoTech">
// Copyright (c) AillieoTech. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;

[RequireComponent(typeof(RectTransform))]
[DisallowMultipleComponent]
public class DyScrollView : ScrollRect
{
    [Tooltip("默认item尺寸")] public Vector2 defaultItemSize;

    [Tooltip("item的模板")] public GameObject itemTemplate;

    // 0001
    protected const int flagScrollDirection = 1;

    [SerializeField] [FormerlySerializedAs("m_layoutType")]
    protected ItemLayoutType layoutType = ItemLayoutType.Vertical;

    // 只保存4个临界index
    protected int[] criticalItemIndex = new int[4];

    // callbacks for items
    public delegate void updateFunc_delegate(int index , RectTransform obj);
    public delegate Vector2 itemSizeFunc_delegate(int index);
    public delegate int itemCountFunc_delegate();
    public delegate RectTransform itemGetFunc_delegate(int index);
    public delegate void itemRecycleFunc_delegate(RectTransform obj);
    private updateFunc_delegate m_updateFunc;
    private itemSizeFunc_delegate m_itemSizeFunc;
    private itemCountFunc_delegate m_itemCountFunc;
    private itemGetFunc_delegate m_itemGetFunc;
    private itemRecycleFunc_delegate m_itemRecycleFune;
    protected Action<int, RectTransform> updateFunc;
    protected Func<int, Vector2> itemSizeFunc;
    protected Func<int> itemCountFunc;
    protected Func<int, RectTransform> itemGetFunc;
    protected Action<RectTransform> itemRecycleFunc;

    private List<ScrollItemWithRect> managedItems = new List<ScrollItemWithRect>();

    private Rect refRect;

    // resource management
    private SimpleObjPool<RectTransform> itemPool = null;

    private int dataCount = 0;

    [Tooltip("初始化时池内item数量")] [SerializeField]
    private int poolSize;

    // status
    private bool initialized = false;
    private int willUpdateData = 0;

    private Vector3[] viewWorldConers = new Vector3[4];
    private Vector3[] rectCorners = new Vector3[2];

    private Transform poolNode;

    private Action rectChangeCallback;//列表大小变化的回调
    // for hide and show
    public enum ItemLayoutType
    {
        // 最后一位表示滚动方向
        Vertical = 0b0001, // 0001
        Horizontal = 0b0010, // 0010
        VerticalThenHorizontal = 0b0100, // 0100
        HorizontalThenVertical = 0b0101, // 0101
    }

    public virtual void SetUpdateFunc(Action<int, RectTransform> func)
    {
        this.updateFunc = func;
    }
    public virtual void SetUpdateDelegate(updateFunc_delegate func)
    {
        this.m_updateFunc = func;
    }
    public virtual void SetItemSizeFunc(Func<int, Vector2> func)
    {
        this.itemSizeFunc = func;
    }
    public virtual void SetItemSizeDelegate(itemSizeFunc_delegate func)
    {
        this.m_itemSizeFunc = func;
    }

    public virtual void SetItemCountFunc(Func<int> func)
    {
        this.itemCountFunc = func;
    }
    public virtual void SetItemCountDelegate(itemCountFunc_delegate func)
    {
        this.m_itemCountFunc = func;
    }
    public void SetItemGetAndRecycleDelegate(itemGetFunc_delegate getFunc, itemRecycleFunc_delegate recycleFunc)
    {
        if (getFunc != null && recycleFunc != null)
        {
            this.m_itemGetFunc = getFunc;
            this.m_itemRecycleFune = recycleFunc;
        }
        else
        {
            this.m_itemGetFunc = null;
            this.m_itemRecycleFune = null;
        }
    }
    public virtual void SetItemTemp(GameObject temp)
    {
        if (this.itemTemplate != null && this.itemTemplate.name == temp.name) return;
        this.itemTemplate = temp;
        //切换clone模版 清理池子
        if (poolNode != null)
        {
            for (int i = 0; i < poolNode.childCount; i++)
            {
                GameObject.Destroy(poolNode.GetChild(i).gameObject);
            }
        }
        if (this.itemPool != null)
        {
            this.itemPool.Purge();
        }
    }

    public void SetItemGetAndRecycleFunc(Func<int, RectTransform> getFunc, Action<RectTransform> recycleFunc)
    {
        if (getFunc != null && recycleFunc != null)
        {
            this.itemGetFunc = getFunc;
            this.itemRecycleFunc = recycleFunc;
        }
        else
        {
            this.itemGetFunc = null;
            this.itemRecycleFunc = null;
        }
    }

    public void ResetAllDelegates()
    {
        this.SetUpdateFunc(null);
        this.SetItemSizeFunc(null);
        this.SetItemCountFunc(null);
        this.SetItemGetAndRecycleFunc(null, null);
        this.SetUpdateDelegate(null);
        this.SetItemSizeDelegate(null);
        this.SetItemCountDelegate(null);
        this.SetItemGetAndRecycleDelegate(null, null);
    }

    public void UpdateData(bool immediately = true)
    {
        if (immediately)
        {
            this.willUpdateData |= 3; // 0011
            this.InternalUpdateData();
        }
        else
        {
            if (this.willUpdateData == 0 && this.IsActive())
            {
                this.StartCoroutine(this.DelayUpdateData());
            }

            this.willUpdateData |= 3;
        }
    }

    public void UpdateDataIncrementally(bool immediately = true)
    {
        if (immediately)
        {
            this.willUpdateData |= 1; // 0001
            this.InternalUpdateData();
        }
        else
        {
            if (this.willUpdateData == 0)
            {
                this.StartCoroutine(this.DelayUpdateData());
            }

            this.willUpdateData |= 1;
        }
    }

    public void ScrollTo(int index)
    {
        this.InternalScrollTo(index);
    }
    public void OnClose()
    {
        for (int i = 0; i < this.managedItems.Count; i++)
        {
            this.RecycleOldItem(this.managedItems[i].item);
        }
        this.initialized = false;
        this.dataCount = 0;
        this.managedItems.Clear();
    }
    public RectTransform GetItemByIndex(int index)
    {
        if (index >= 0 && index < this.dataCount)
        {
            return this.managedItems[index].item;
        }
        return null;
    }

    protected override void OnEnable()
    {
        base.OnEnable();
        if (this.willUpdateData != 0)
        {
            this.StartCoroutine(this.DelayUpdateData());
        }
    }

    protected override void OnDisable()
    {
       
        base.OnDisable();
    }

    protected virtual void InternalScrollTo(int index)
    {
        index = Mathf.Clamp(index, 0, Mathf.Max(0, this.dataCount - 1));
        Vector2 pos = Vector2.zero;
        if (this.managedItems.Count > 0 && this.managedItems[index] != null)
        {
            this.EnsureItemRect(index);
            Rect r = this.managedItems[index].rect;
            pos = new Vector2(r.xMin, r.yMax);
        }
       
        var dir = (int) this.layoutType & flagScrollDirection;
        if (dir == 1)
        {
            // vertical
            var value = 1 - (-pos.y / (this.content.sizeDelta.y - this.refRect.height));
            this.SetNormalizedPosition(value, 1);
            this.ResetCriticalItems();
        }
        else
        {
            // horizontal
            var value = pos.x / (this.content.sizeDelta.x - this.refRect.width);
            this.SetNormalizedPosition(value, 0);
            this.ResetCriticalItems();
        }
    }

    protected override void SetContentAnchoredPosition(Vector2 position)
    {
        base.SetContentAnchoredPosition(position);
        this.UpdateCriticalItems();
    }

    protected void EnsureItemRect(int index)
    {
        if (this.managedItems.Count == 0) return;
        if (!this.managedItems[index].rectDirty)
        {
            // 已经是干净的了
           
            return;
        }

        ScrollItemWithRect firstItem = this.managedItems[0];
        if (firstItem.rectDirty)
        {
            Vector2 firstSize = this.GetItemSize(0);
            firstItem.rect = CreateWithLeftTopAndSize(Vector2.zero, firstSize);
            firstItem.rectDirty = false;
        }

        // 当前item之前的最近的已更新的rect
        var nearestClean = 0;
        for (var i = index; i >= 0; --i)
        {
            if (!this.managedItems[i].rectDirty)
            {
                nearestClean = i;
                break;
            }
        }

        // 需要更新 从 nearestClean 到 index 的尺寸
        Rect nearestCleanRect = this.managedItems[nearestClean].rect;
        Vector2 curPos = GetLeftTop(nearestCleanRect);
        Vector2 size = nearestCleanRect.size;
        this.MovePos(ref curPos, size);

        for (var i = nearestClean + 1; i <= index; i++)
        {
            size = this.GetItemSize(i);
            this.managedItems[i].rectDirty = false;
            var lastsize = this.GetItemSize(i - 1);
            Rect lastRect = this.managedItems[i - 1].rect;
            Vector2 lastPos = GetLeftTop(lastRect);
            switch (this.layoutType)
            {
                case ItemLayoutType.VerticalThenHorizontal:
                    if (size.y > refRect.height && curPos.x == lastPos.x)
                    {
                        curPos.y = 0;
                        curPos.x += size.x;
                    }
                    break;
                case ItemLayoutType.HorizontalThenVertical:
                    if (size.x + lastsize.x > refRect.width && curPos.y == lastPos.y)
                    {
                        curPos.x = 0;
                        curPos.y -= lastsize.y;
                    }
                    break;
                default:
                    break;
            }

            this.managedItems[i].rect = CreateWithLeftTopAndSize(curPos, size);
            this.MovePos(ref curPos, size);
        }

        var range = new Vector2(Mathf.Abs(curPos.x), Mathf.Abs(curPos.y));
        switch (this.layoutType)
        {
            case ItemLayoutType.VerticalThenHorizontal:
                range.x += size.x;
                range.y = this.refRect.height;
                break;
            case ItemLayoutType.HorizontalThenVertical:
                range.x = this.refRect.width;
                if (curPos.x != 0)
                {
                    range.y += size.y;
                }

                break;
            default:
                break;
        }

        this.content.sizeDelta = range;
    }

    protected override void OnDestroy()
    {
        if (this.itemPool != null)
        {
            this.itemPool.Purge();
        }
    }

    protected Rect GetItemLocalRect(int index)
    {
        if (index >= 0 && index < this.dataCount)
        {
            this.EnsureItemRect(index);
            return this.managedItems[index].rect;
        }

        return (Rect) default;
    }

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        var dir = (int) this.layoutType & flagScrollDirection;
        if (dir == 1)
        {
            // vertical
            if (this.horizontalScrollbar != null)
            {
                this.horizontalScrollbar.gameObject.SetActive(false);
                this.horizontalScrollbar = null;
            }
        }
        else
        {
            // horizontal
            if (this.verticalScrollbar != null)
            {
                this.verticalScrollbar.gameObject.SetActive(false);
                this.verticalScrollbar = null;
            }
        }

        base.OnValidate();
    }
#endif

    #region 内部函数
    private static Vector2 GetLeftTop(Rect rect)
    {
        Vector2 ret = rect.position;
        ret.y += rect.size.y;
        return ret;
    }

    private static Rect CreateWithLeftTopAndSize(Vector2 leftTop, Vector2 size)
    {
        Vector2 leftBottom = leftTop - new Vector2(0, size.y);
        return new Rect(leftBottom, size);
    }

    private IEnumerator DelayUpdateData()
    {
        yield return new WaitForEndOfFrame();
        this.InternalUpdateData();
    }

    private void InternalUpdateData()
    {
        if (!this.IsActive())
        {
            this.willUpdateData |= 3;
            return;
        }

        if (!this.initialized)
        {
            this.InitScrollView();
        }

        var newDataCount = 0;
        var keepOldItems = (this.willUpdateData & 2) == 0;

        if (this.itemCountFunc != null)
        {
            newDataCount = this.itemCountFunc();
        }else if(this.m_itemCountFunc != null)
        {
            newDataCount = this.m_itemCountFunc();
        }

        if (newDataCount != this.managedItems.Count)
        {
            if (this.managedItems.Count < newDataCount)
            {
                // 增加
                if (!keepOldItems)
                {
                    foreach (var itemWithRect in this.managedItems)
                    {
                        // 重置所有rect
                        itemWithRect.rectDirty = true;
                    }
                }

                while (this.managedItems.Count < newDataCount)
                {
                    this.managedItems.Add(new ScrollItemWithRect());
                }
            }
            else
            {
                // 减少 保留空位 避免GC
                for (int i = 0, count = this.managedItems.Count; i < count; ++i)
                {
                    if (i < newDataCount)
                    {
                        // 重置所有rect
                        if (!keepOldItems)
                        {
                            this.managedItems[i].rectDirty = true;
                        }

                        if (i == newDataCount - 1)
                        {
                            this.managedItems[i].rectDirty = true;
                        }
                    }

                    // 超出部分 清理回收item
                    if (i >= newDataCount)
                    {
                        this.managedItems[i].rectDirty = true;
                        if (this.managedItems[i].item != null)
                        {
                            this.RecycleOldItem(this.managedItems[i].item);
                            this.managedItems[i].item = null;
                        }
                    }
                }
            }
        }
        else
        {
            if (!keepOldItems)
            {
                for (int i = 0, count = this.managedItems.Count; i < count; ++i)
                {
                    // 重置所有rect
                    this.managedItems[i].rectDirty = true;
                }
            }
        }

        this.dataCount = newDataCount;

        this.ResetCriticalItems();

        this.willUpdateData = 0;
    }

    private void ResetCriticalItems()
    {
        bool hasItem, shouldShow;
        int firstIndex = -1, lastIndex = -1;

        for (var i = 0; i < this.dataCount; i++)
        {
            hasItem = this.managedItems.Count > 0 && this.managedItems[i].item != null;
            shouldShow = this.ShouldItemSeenAtIndex(i);

            if (shouldShow)
            {
                if (firstIndex == -1)
                {
                    firstIndex = i;
                }

                lastIndex = i;
            }

            if (hasItem && shouldShow)
            {
                // 应显示且已显示
                this.SetDataForItemAtIndex(this.managedItems[i].item, i);
                continue;
            }

            if (hasItem == shouldShow)
            {
                // 不应显示且未显示
                // if (firstIndex != -1)
                // {
                //     // 已经遍历完所有要显示的了 后边的先跳过
                //     break;
                // }
                continue;
            }

            if (hasItem && !shouldShow)
            {
                // 不该显示 但是有
                this.RecycleOldItem(this.managedItems[i].item);
                this.managedItems[i].item = null;
                continue;
            }

            if (shouldShow && !hasItem)
            {
                // 需要显示 但是没有
                RectTransform item = this.GetNewItem(i);
                this.OnGetItemForDataIndex(item, i);
                this.managedItems[i].item = item;
                continue;
            }
        }

        // content.localPosition = Vector2.zero;
        this.criticalItemIndex[CriticalItemType.UpToHide] = firstIndex;
        this.criticalItemIndex[CriticalItemType.DownToHide] = lastIndex;
        this.criticalItemIndex[CriticalItemType.UpToShow] = Mathf.Max(firstIndex - 1, 0);
        this.criticalItemIndex[CriticalItemType.DownToShow] = Mathf.Min(lastIndex + 1, this.dataCount - 1);
    }

    private RectTransform GetCriticalItem(int type)
    {

        var index = this.criticalItemIndex[type];
        if (index >= 0 && index < this.dataCount)
        {
            return this.managedItems[index].item;
        }

        return null;
    }

    private void UpdateCriticalItems()
    {
        var dirty = true;

        while (dirty)
        {
            dirty = false;

            for (int i = CriticalItemType.UpToHide; i <= CriticalItemType.DownToShow; i++)
            {
                if (i <= CriticalItemType.DownToHide)
                {
                    // 隐藏离开可见区域的item
                    dirty = dirty || this.CheckAndHideItem(i);
                }
                else
                {
                    // 显示进入可见区域的item
                    dirty = dirty || this.CheckAndShowItem(i);
                }
            }
        }
    }

    private bool CheckAndHideItem(int criticalItemType)
    {
        RectTransform item = this.GetCriticalItem(criticalItemType);
        var criticalIndex = this.criticalItemIndex[criticalItemType];
        if (item != null && !this.ShouldItemSeenAtIndex(criticalIndex))
        {
            this.RecycleOldItem(item);
            this.managedItems[criticalIndex].item = null;

            if (criticalItemType == CriticalItemType.UpToHide)
            {
                // 最上隐藏了一个
                this.criticalItemIndex[criticalItemType + 2] =
                    Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
                this.criticalItemIndex[criticalItemType]++;
            }
            else
            {
                // 最下隐藏了一个
                this.criticalItemIndex[criticalItemType + 2] =
                    Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType + 2]);
                this.criticalItemIndex[criticalItemType]--;
            }

            this.criticalItemIndex[criticalItemType] =
                Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);

            if (this.criticalItemIndex[CriticalItemType.UpToHide] > this.criticalItemIndex[CriticalItemType.DownToHide])
            {
                // 偶然的情况 拖拽超出一屏
                this.ResetCriticalItems();
                return false;
            }

            return true;
        }

        return false;
    }

    private bool CheckAndShowItem(int criticalItemType)
    {
        RectTransform item = this.GetCriticalItem(criticalItemType);
        var criticalIndex = this.criticalItemIndex[criticalItemType];

        if (item == null && this.ShouldItemSeenAtIndex(criticalIndex))
        {
            RectTransform newItem = this.GetNewItem(criticalIndex);
            this.OnGetItemForDataIndex(newItem, criticalIndex);
            this.managedItems[criticalIndex].item = newItem;

            if (criticalItemType == CriticalItemType.UpToShow)
            {
                // 最上显示了一个
                this.criticalItemIndex[criticalItemType - 2] =
                    Mathf.Min(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
                this.criticalItemIndex[criticalItemType]--;
            }
            else
            {
                // 最下显示了一个
                this.criticalItemIndex[criticalItemType - 2] =
                    Mathf.Max(criticalIndex, this.criticalItemIndex[criticalItemType - 2]);
                this.criticalItemIndex[criticalItemType]++;
            }

            this.criticalItemIndex[criticalItemType] =
                Mathf.Clamp(this.criticalItemIndex[criticalItemType], 0, this.dataCount - 1);

            if (this.criticalItemIndex[CriticalItemType.UpToShow] >=
                this.criticalItemIndex[CriticalItemType.DownToShow])
            {
                // 偶然的情况 拖拽超出一屏
                this.ResetCriticalItems();
                return false;
            }

            return true;
        }

        return false;
    }

    private bool ShouldItemSeenAtIndex(int index)
    {
        if (this.managedItems.Count == 0 || index < 0 || index >= this.dataCount)
        {
            return false;
        }

        this.EnsureItemRect(index);
        return new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size).Overlaps(
            this.managedItems[index].rect);
    }

    private bool ShouldItemFullySeenAtIndex(int index)
    {
        if (index < 0 || index >= this.dataCount)
        {
            return false;
        }

        this.EnsureItemRect(index);
        return this.IsRectContains(new Rect(this.refRect.position - this.content.anchoredPosition, this.refRect.size),
            this.managedItems[index].rect);
    }

    private bool IsRectContains(Rect outRect, Rect inRect, bool bothDimensions = false)
    {
        if (bothDimensions)
        {
            var xContains = (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
            var yContains = (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
            return xContains && yContains;
        }
        else
        {
            var dir = (int) this.layoutType & flagScrollDirection;
            if (dir == 1)
            {
                // 垂直滚动 只计算y向
                return (outRect.yMax >= inRect.yMax) && (outRect.yMin <= inRect.yMin);
            }
            else
            {
                // = 0
                // 水平滚动 只计算x向
                return (outRect.xMax >= inRect.xMax) && (outRect.xMin <= inRect.xMin);
            }
        }
    }

    private void InitPool()
    {
        poolNode = transform.Find("POOL");
        if (poolNode == null)
        {
            poolNode = new GameObject("POOL").transform;
        }
        poolNode.gameObject.SetActive(false);
        poolNode.transform.SetParent(this.transform, false);
        if(this.itemPool == null)
            this.itemPool = new SimpleObjPool<RectTransform>(
                this.poolSize,
                (RectTransform item) =>
                {
                    item.transform.SetParent(poolNode.transform, false);
                },
                () =>
                {
                    GameObject itemObj = Instantiate(this.itemTemplate);
                    RectTransform item = itemObj.GetComponent<RectTransform>();
                    itemObj.transform.SetParent(poolNode.transform, false);

                    item.anchorMin = Vector2.up;
                    item.anchorMax = Vector2.up;
                    item.pivot = Vector2.zero;

                    itemObj.SetActive(true);
                    return item;
                });
    }

    private void OnGetItemForDataIndex(RectTransform item, int index)
    {
        this.SetDataForItemAtIndex(item, index);
        item.transform.SetParent(this.content, false);
    }

    private void SetDataForItemAtIndex(RectTransform item, int index)
    {
        if (this.updateFunc != null)
        {
            this.updateFunc(index, item);
        }
        else if(this.m_updateFunc != null)
        {
            this.m_updateFunc(index, item);
        }
        this.SetPosForItemAtIndex(item, index);
    }

    private void SetPosForItemAtIndex(RectTransform item, int index)
    {
        this.EnsureItemRect(index);
        Rect r = this.managedItems[index].rect;
        item.localPosition = r.position;
        item.sizeDelta = r.size;
    }

    public Vector2 GetItemSize(int index)
    {
        if (index >= 0 && index <= this.dataCount)
        {
            if (this.itemSizeFunc != null)
            {
                return this.itemSizeFunc(index);
            }
            else if(this.m_itemSizeFunc != null)
            {
                return this.m_itemSizeFunc(index);
            }
        }

        return this.defaultItemSize;
    }

    private RectTransform GetNewItem(int index)
    {
        RectTransform item;
        if (this.itemGetFunc != null)
        {
            item = this.itemGetFunc(index);
        }
        else if(this.m_itemGetFunc != null)
        {
            item = this.m_itemGetFunc(index);
        }
        else
        {
            
            item = this.itemPool.Get();
            //if (item.name != this.itemTemplate.name)
            //{
            //    if (this.itemPool != null)
            //    {
            //        this.itemPool.Purge();
            //    }
            //    item = this.itemPool.Get();
            //}
        }

        return item;
    }

    private void RecycleOldItem(RectTransform item)
    {
        if (this.itemRecycleFunc != null)
        {
            this.itemRecycleFunc(item);
        }
        else if(this.m_itemRecycleFune != null)
        {
            this.m_itemRecycleFune(item);
        }
        else
        {
            this.itemPool.Recycle(item);
        }
    }

    private void InitScrollView()
    {
        this.initialized = true;
        this.managedItems = new List<ScrollItemWithRect>();
        // 根据设置来控制原ScrollRect的滚动方向
        var dir = (int) this.layoutType & flagScrollDirection;
        this.vertical = dir == 1;
        this.horizontal = dir == 0;

        this.content.pivot = Vector2.up;
        this.content.anchorMin = Vector2.up;
        this.content.anchorMax = Vector2.up;
        this.content.anchoredPosition = Vector2.zero;

        this.InitPool();
        this.UpdateRefRect();
    }

    // refRect是在Content节点下的 viewport的 rect
    public void UpdateRefRect()
    {
        /*
         *  WorldCorners
         *
         *    1 ------- 2
         *    |         |
         *    |         |
         *    0 ------- 3
         *
         */

        if (!CanvasUpdateRegistry.IsRebuildingLayout())
        {
            Canvas.ForceUpdateCanvases();
        }

        this.viewRect.GetWorldCorners(this.viewWorldConers);
        this.rectCorners[0] = this.content.transform.InverseTransformPoint(this.viewWorldConers[0]);
        this.rectCorners[1] = this.content.transform.InverseTransformPoint(this.viewWorldConers[2]);
        this.refRect = new Rect((Vector2) this.rectCorners[0] - this.content.anchoredPosition,
            this.rectCorners[1] - this.rectCorners[0]);
    }

    Vector2 lastPos = Vector2.zero;

    private void MovePos(ref Vector2 pos, Vector2 size)
    {
        // 注意 所有的rect都是左下角为基准
        switch (this.layoutType)
        {
            case ItemLayoutType.Vertical:
                // 垂直方向 向下移动
                pos.y -= size.y;
                break;
            case ItemLayoutType.Horizontal:
                // 水平方向 向右移动
                pos.x += size.x;
                break;
            case ItemLayoutType.VerticalThenHorizontal:
                pos.y -= size.y;
                if (pos.y - size.y < -this.refRect.height)
                {
                    pos.y = 0;
                    pos.x += size.x;
                }

                break;
            case ItemLayoutType.HorizontalThenVertical:
                pos.x += size.x;
                if (pos.x + size.x > this.refRect.width)
                {
                    pos.x = 0;
                    pos.y -= size.y;
                }

                break;
            default:
                break;
        }

        lastPos = size;
    }
     
    protected override void OnRectTransformDimensionsChange()
    {
        base.OnRectTransformDimensionsChange();
        rectChangeCallback?.Invoke();
    }
    public void SetRectChangeCallback(Action callback)
    {
        rectChangeCallback = callback;
    }
    // const int 代替 enum 减少 (int)和(CriticalItemType)转换
    protected static class CriticalItemType
    {
        public static byte UpToHide = 0;
        public static byte DownToHide = 1;
        public static byte UpToShow = 2;
        public static byte DownToShow = 3;
    }

    private class ScrollItemWithRect
    {
        // scroll item 身上的 RectTransform组件
        public RectTransform item;

        // scroll item 在scrollview中的位置
        public Rect rect;

        // rect 是否需要更新
        public bool rectDirty = true;
    }
    #endregion

}

